/*
 * sr_type.c - EnCluster Standard Representation Type Utility Module
 * Clunix, glory@clunix.com, yjlee@clunix.com, 2001.6.5,25.
 *
 * Copyright (c) 2001-2005 by Clunix Inc. All Rights Reserved.
 */

#include "config.h"

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#include "sr.h"
#include "sr_p.h"

const sr_t	SR_NULL = {
	ST_NULL
};

ECMBOOL
srt_is_primitive(srt_t srt)
{
	if ((srt & (SR_VIRTUAL | SR_VHMS | SR_HOMOSET | SR_REF)) == 0)
		return ECM_TRUE;
	return ECM_FALSE;
}

ECMBOOL
srt_is_base(srt_t srt)
{
	if (srt_is_primitive(srt) && SR_SUBTYPE(srt) == 0)
		return ECM_TRUE;
	return ECM_FALSE;
}

ECMBOOL
srt_is_same(srt_t srt1, srt_t srt2)
{
	if (SR_REALTYPE(srt1) == SR_REALTYPE(srt2))
		return ECM_TRUE;
	return ECM_FALSE;
}

ECMBOOL
srt_is_member(srt_t srt_group, srt_t srt_mbr)
{
	srt_t	srt_grpmbr;

	if (srt_group == ST_NULL || srt_mbr == ST_NULL)
		return ECM_FALSE;
	if (!srt_is_group(srt_group))
		return ECM_FALSE;
	srt_grpmbr = srt_get_member_type(srt_group);
	if (srt_grpmbr == ST_NULL)
		return ECM_TRUE;
	if (srt_mbr & SR_VHMS)
		return ECM_FALSE;
	if (srt_is_HMS(srt_group) && srt_is_virtual(srt_mbr))
		return ECM_FALSE;
	if (srt_is_vHMS(srt_group) && srt_is_virtual(srt_mbr))
		srt_mbr = srt_get_real_type(srt_mbr);
	return srt_is_compatible(srt_mbr, srt_grpmbr);
}

void
srv_free(srt_t srt, sr_val_t val)
{
	if (srt_is_group(srt)) {
		sr_free(val.dir);
		return;
	}
	else if (srt_is_virtual(srt))
		return;
	else if (srt_is_ref(srt)) {
		sr_free(val.ptr);
		return;
	}

	switch (SR_TYPECLASS(srt)) {
	case SR_TYPECLS_STR:
		ecm_free(val.str);
		return;
	case SR_TYPECLS_OCT:
		ecm_free(val.oct.data);
		return;
	default:
		return;
	}
}

/* SR value temporary creators; does not duplicate value ----------------- */

sr_val_t
sr_val_int(ecm_int32 ival)
{
	sr_val_t	v;
	v.ival = ival;
	return v;
}

sr_val_t
sr_val_ll(ecm_int64 lval)
{
	sr_val_t	v;
	v.lval = lval;
	return v;
}

sr_val_t
sr_val_dbl(ecm_double dval)
{
	sr_val_t	v;
	v.dval = dval;
	return v;
}

sr_val_t
sr_val_str(char *str)
{
	sr_val_t	v;
	v.str = str;
	return v;
}

sr_val_t
sr_val_oct(ecm_size_t size, void *data)
{
	sr_val_t	v;
	v.oct.size = size;
	v.oct.data = data;
	return v;
}

sr_val_t
sr_val_ptr(ecm_cptr_t ptr)
{
	sr_val_t	v;
	v.ptr = (ecm_ptr_t)ptr;
	return v;
}

sr_val_t
sr_val_dir(const sr_t *sr_dir)
{
	sr_val_t	v;
	v.dir = (sr_t *)sr_dir;
	return v;
}

sr_val_t
sr_val_bool(ECMBOOL bval)
{
	sr_val_t	v;
	v.bval = bval;
	return v;
}


/* SR value equality test ------------------------------------------------ */

static ECM_INLINE int
cmp_srt(srt_t srt1, srt_t srt2)
{
	if (srt1 == srt2)
		return 0;
	if (srt1 > srt2)
		return 1;
	return -1;
}

static ECM_INLINE int
val_group_cmp(sr_t *sr_1, sr_t *sr_2)
{
	sr_t	*sr_c1, *sr_c2;
	struct list_head	*lp1, *lp2;
	int	ret;

	if (!sr_is_group(sr_1) && !sr_is_group(sr_2))
		return 0;
	if (!sr_is_group(sr_1))
		return -1;
	if (!sr_is_group(sr_2))
		return 1;

	if ((ret = cmp_srt(sr_1->type, sr_2->type)) != 0)
		return ret;

	for (lp1 = sr_1->srv_head.next, lp2 = sr_2->srv_head.next;
	     lp1 != &sr_1->srv_head && lp2 != &sr_2->srv_head;
	     lp1 = lp1->next, lp2 = lp2->next) {
		sr_c1 = list_entry(lp1, sr_t, list);
		sr_c2 = list_entry(lp2, sr_t, list);
			
		if ((ret = cmp_srt(sr_c1->type, sr_c2->type)) != 0 ||
		    (ret = ecm_strcmp(sr_c1->name, sr_c2->name)) != 0)
			return ret;
		if (sr_is_group(sr_c1))
			ret = srv_cmp(sr_c1->type, sr_val_dir(sr_c1), sr_val_dir(sr_c2));
		else
			ret = srv_cmp(sr_c1->type, sr_c1->srv_val, sr_c2->srv_val);
		if (ret != 0)
			return ret;
	}
	if (lp1 == &sr_1->srv_head && lp2 == &sr_2->srv_head)
		return 0;
	else if (lp1 == &sr_1->srv_head)
		return -1;
	return 1;
}

int
srv_cmp(srt_t srt, sr_val_t val1, sr_val_t val2)
{
	if (srt_is_ref(srt)) {
		if (sr_is_same_sr_LNP(val1.ptr, val2.ptr))
			return 0;
		return 1;
	}
	switch (SR_TYPECLASS(srt)) {
	case SR_TYPECLS_INT:
		return (val1.ival - val2.ival);
	case SR_TYPECLS_LL:
		return (int)(val1.lval - val2.lval);
	case SR_TYPECLS_DBL: {
		if (val1.dval > val2.dval)
			return 1;
		else if (val1.dval < val2.dval)
			return -1;
		return 0;
	}
	case SR_TYPECLS_STR:
		return ecm_strcmp(val1.str, val2.str);
	case SR_TYPECLS_OCT:
		if (val1.oct.size == val2.oct.size)
			return ecm_memcmp(val1.oct.data, val2.oct.data, val1.oct.size);
		return val1.oct.size - val2.oct.size;
	case SR_TYPECLS_PTR:
		if (val1.ptr == val2.ptr)
			return 0;
		return 1;
	case SR_TYPECLS_BOOL:
		if (val1.bval == val2.bval)
			return 0;
		return 1;
	case SR_TYPECLS_DIR:
		return val_group_cmp(val1.dir, val2.dir);
	default:
		return 0;
	}
}

static int
srt_null_print(srt_t srt, char *buf, ecm_size_t *pbuflen, sr_val_t srv)
{
	if (*pbuflen < 4) {
		*pbuflen = 4;
		return -1;
	}
	memcpy(buf, "null", 4);
	return 4;
}

static ECMBOOL
srt_null_scan(srt_t srt, char *asrv, sr_val_t *psrv)
{
	if (ecm_strcmp(asrv, "null") == 0)
		return ECM_TRUE;
	return ECM_FALSE;
}

static int
srt_int_print(srt_t srt, char *buf, ecm_size_t *pbuflen, sr_val_t srv)
{
	char	*asrvbuf;
	int	asrvlen;

	asrvlen = ecm_asprintf(&asrvbuf, "%d", srv.ival);
	if (*pbuflen < (ecm_size_t)asrvlen) {
		*pbuflen = asrvlen;
		ecm_free(asrvbuf);
		return -1;
	}
	memcpy(buf, asrvbuf, asrvlen);
	ecm_free(asrvbuf);
	return asrvlen;
}

static ECMBOOL
srt_int_scan(srt_t srt, char *asrv, sr_val_t *psrv)
{
	ecm_int32	ival;

	if (ecm_sscanf(asrv, "%d", &ival) != 1)
		return ECM_FALSE;
	psrv->ival = ival;
	return ECM_TRUE;
}

static int
srt_ll_print(srt_t srt, char *buf, ecm_size_t *pbuflen, sr_val_t srv)
{
	char	*asrvbuf;
	int	asrvlen;

	asrvlen = ecm_asprintf(&asrvbuf, "%lld", srv.lval);
	if (*pbuflen < (ecm_size_t)asrvlen) {
		*pbuflen = asrvlen;
		ecm_free(asrvbuf);
		return -1;
	}
	memcpy(buf, asrvbuf, asrvlen);
	ecm_free(asrvbuf);
	return asrvlen;
}

static ECMBOOL
srt_ll_scan(srt_t srt, char *asrv, sr_val_t *psrv)
{
	ecm_int64	lval;

	if (ecm_sscanf(asrv, "%I64d", &lval) != 1)
		return ECM_FALSE;
	psrv->lval = lval;
	return ECM_TRUE;
}

static int
srt_dbl_print(srt_t srt, char *buf, ecm_size_t *pbuflen, sr_val_t srv)
{
	char	*asrvbuf;
	int	asrvlen;

	asrvlen = ecm_asprintf(&asrvbuf, "%.8f", srv.dval);
	if (*pbuflen < (ecm_size_t)asrvlen) {
		*pbuflen = asrvlen;
		ecm_free(asrvbuf);
		return -1;
	}
	memcpy(buf, asrvbuf, asrvlen);
	ecm_free(asrvbuf);
	return asrvlen;
}

static ECMBOOL
srt_dbl_scan(srt_t srt, char *asrv, sr_val_t *psrv)
{
	ecm_double	dval, denominator;
	char		*p;
	ECMBOOL		point = ECM_FALSE;
	ECMBOOL		minus = ECM_FALSE;

	p = asrv;
	if (*p == '-') {
		minus = ECM_TRUE;
		p++;
	}
	dval = 0;
	denominator = 0.1;
	while (*p) {
		if (*p >= '0' && *p <= '9') {
			if (point) {
				dval += (*p - '0') * denominator;
				denominator *= 0.1;
			}
			else {
				dval *= 10;
				dval += (*p - '0');
			}
		}
		else if (*p == '.')
			point = ECM_TRUE;
		else
			return ECM_FALSE;
		p++;
	}
	if (minus)
		psrv->dval = dval * (-1.0);
	else
		psrv->dval = dval;
	return ECM_TRUE;
}

static ECM_INLINE int
get_str_asrvlen(const char *str)
{
	int	len = 0;
	
	if (str == NULL)
		return 0;
	while (*str) {
		if (*str == '\\')
			len += 2;
		else
			len++;
		str++;
	}
	return len;
}

static ECM_INLINE void
set_str_asrv(char *buf, const char *str)
{
	while (*str) {
		if (*str == '\\') {
			*buf++ = '\\';
		}
		*buf++ = *str++;
	}
}

static int
srt_str_print(srt_t srt, char *buf, ecm_size_t *pbuflen, sr_val_t srv)
{
	ecm_size_t	size;

	if (srv.str == NULL)
		return 0;

	size = ecm_strlen(srv.str);
	if (*pbuflen < size) {
		*pbuflen = size;
		return -1;
	}
	memcpy(buf, srv.str, size);
	return size;
}

static ECMBOOL
srt_str_scan(srt_t srt, char *asrv, sr_val_t *psrv)
{
	psrv->str = ecm_strdup(asrv);
	return ECM_TRUE;
}

static int
srt_oct_print(srt_t srt, char *buf, ecm_size_t *pbuflen, sr_val_t srv)
{
	ecm_uint32	i;
	ecm_size_t	size = srv.oct.size;
	unsigned char	*data = srv.oct.data;

	if (srv.oct.size == 0)
		return 0;

	if (*pbuflen < size * 2) {
		*pbuflen = size * 2;
		return -1;
	}
	for (i = 0; i < size; i++) {
		sprintf(buf, "%02x", data[i]);
		buf += 2;
	}
	return size * 2;
}

static ECMBOOL
srt_oct_scan(srt_t srt, char *asrv, sr_val_t *psrv)
{
	ecm_uint8	*data = NULL;
	ecm_size_t	size;

	size = ecm_strlen(asrv) / 2;
	if (size > 0) {
		ecm_uint32	i, hexval;
		ecm_uint8	*p;

		p = data = (ecm_uint8 *)ecm_malloc(size);
		for (i = 0; i < size; i++) {
			if (ecm_sscanf(asrv, "%02x", &hexval) != 1) {
				ecm_free(data);
				return ECM_FALSE;
			}
			*p++ = (ecm_uint8)hexval;
			asrv += 2;
		}
	}
	psrv->oct.size = size;
	psrv->oct.data = data;
	return ECM_TRUE;
}

static ECM_INLINE sr_t *
scan_dir_asrv(char *dir_asrv)
{
	sr_t		*sr = NULL;
	yyscan_t	scanner;
	asr_input_t	input;

	if (dir_asrv == NULL)
		return NULL;

	srplex_init(&scanner);
	srpset_extra(&input, scanner);

	SR_DEBUG_FLEX(scanner);

	input.instr = dir_asrv;
	input.instr_len = ecm_strlen(dir_asrv);
	input.instr_ptr = 0;

	srpparse(scanner, &sr);
	srplex_destroy(scanner);

	return sr;
}

ECMBOOL
srt_dir_scan(srt_t srt, char *asrv, sr_val_t *psrv)
{
	sr_t	*sr_val;

	sr_val = scan_dir_asrv(asrv);
	if (sr_val == NULL)
		return ECM_FALSE;
	psrv->dir = sr_val;
	return ECM_TRUE;
}

static int
srt_ptr_print(srt_t srt, char *buf, ecm_size_t *pbuflen, sr_val_t srv)
{
	char	*asrvbuf;
	int	asrvlen;
	
	asrvlen = ecm_asprintf(&asrvbuf, "%p", srv.ptr);
	if (*pbuflen < (ecm_size_t)asrvlen) {
		*pbuflen = asrvlen;
		ecm_free(asrvbuf);
		return -1;
	}
	memcpy(buf, asrvbuf, asrvlen);
	ecm_free(asrvbuf);
	return asrvlen;
}

static ECMBOOL
srt_ptr_scan(srt_t srt, char *asrv, sr_val_t *psrv)
{
	ecm_ptr_t	ptr;

	if (ecm_sscanf(asrv, "%p", &ptr) != 1)
		return ECM_FALSE;
	psrv->ptr = ptr;
	return ECM_TRUE;
}

static int
srt_bool_print(srt_t srt, char *buf, ecm_size_t *pbuflen, sr_val_t srv)
{
	char	*boolstr;
	int	asrvlen;

	if (srv.bval) {
		asrvlen = 4;
		boolstr = "true";
	}
	else {
		asrvlen = 5;
		boolstr = "false";
	}
	if (*pbuflen < (ecm_size_t)asrvlen) {
		*pbuflen = asrvlen;
		return -1;
	}
	memcpy(buf, boolstr, asrvlen);
	return asrvlen;
}

static ECMBOOL
srt_bool_scan(srt_t srt, char *asrv, sr_val_t *psrv)
{
	if (strcmp(asrv, "true") == 0)
		psrv->bval = ECM_TRUE;
	else if (strcmp(asrv, "false") == 0)
		psrv->bval = ECM_FALSE;
	else
		return ECM_FALSE;
	return ECM_TRUE;
}

ECMBOOL
srt_is_enum(srt_t srt)
{
	const sr_type_t	*srtinfo;
	
	if (SR_TYPECLASS(srt) == SR_TYPECLS_INT &&
	    (srtinfo = srt_get_info(srt)) != NULL &&
	    srtinfo->parser.scan == srt_enum_scan)
		return ECM_TRUE;
	return ECM_FALSE;
}

int
srt_get_enum_count(const srt_t srt)
{
	const sr_type_t	*srtinfo;

	if (!srt_is_enum(srt))
		return -1;
	srtinfo = srt_get_info(srt);
	return srtinfo->priv.enums.nenum;
}

ECMBOOL
srt_is_bitwise(srt_t srt)
{
	const sr_type_t	*srtinfo;
	
	if (SR_TYPECLASS(srt) == SR_TYPECLS_INT &&
	    (srtinfo = srt_get_info(srt)) != NULL &&
	    srtinfo->parser.scan == srt_bitwise_scan)
		return ECM_TRUE;
	return ECM_FALSE;
}

int
srt_get_bitwise_count(const srt_t srt)
{
	const sr_type_t	*srtinfo;

	if (!srt_is_bitwise(srt))
		return -1;
	srtinfo = srt_get_info(srt);
	return srtinfo->priv.enums.nenum;
}

static sr_type_t sr_null_type = {
	"&null", "&null*", 4, { srt_null_print, srt_null_scan }
};

static sr_type_t *sr_null_types[] = {
	&sr_null_type
};

static const char	*enumApplyOpts[] = {
	"one", "child", "recur", "noderef", "notthis", "crypt", "ext"
};

static const char	*enumLNItypes[] = {
	"None", "Name", "All", "Any", "Dot", "DotDot"
};

static sr_type_t sr_int_type = {
	"&int", "&int*", 3, { srt_int_print, srt_int_scan }
};

static sr_type_t sr_applyopt_type = {
	"&applyopt", "&applyopt*", 8, { srt_bitwise_print, srt_bitwise_scan }, NULL, NULL,
#ifndef _WIN32
	{ { 0, 7, enumApplyOpts } }
#else
	{ 0, 7, enumApplyOpts }
#endif
};

static sr_type_t sr_LNItype_type = {
	"&LNItype", "&LNItype*", 7, { srt_enum_print, srt_enum_scan }, NULL, NULL,
#ifndef _WIN32
	{ { 0, 6, enumLNItypes } }
#else
	{ 0, 6, enumLNItypes }
#endif
};

static sr_type_t sr_srt_type = {
	"&srt", "&srt*", 3, { srt_int_print, srt_int_scan }
};

static sr_type_t *sr_int_types[] = {
	&sr_int_type, &sr_applyopt_type, &sr_LNItype_type, &sr_srt_type
};

static sr_type_t sr_ll_type = {
	"&ll", "&ll*", 2, { srt_ll_print, srt_ll_scan }
};

static sr_type_t *sr_ll_types[] = {
	&sr_ll_type
};

static sr_type_t sr_dbl_type = {
	"&dbl", "&dbl*", 3, { srt_dbl_print, srt_dbl_scan }
};

static sr_type_t *sr_dbl_types[] = {
	&sr_dbl_type
};

static sr_type_t sr_str_type = {
	"&str", "&str*", 3, { srt_str_print, srt_str_scan }
};

static sr_type_t sr_LNPSTR_type = {
	"&LNPSTR", "&LNPSTR*", 6, { srt_str_print, srt_str_scan }
};

static sr_type_t *sr_str_types[] = {
	&sr_str_type, &sr_LNPSTR_type
};

static sr_type_t sr_oct_type = {
	"&oct", "&oct*", 3, { srt_oct_print, srt_oct_scan },
};

static sr_type_t *sr_oct_types[] = {
	&sr_oct_type
};

static sr_type_t sr_dir_type = {
	"&dir", "&dir*", 3, { NULL, srt_dir_scan }
};
static sr_type_t sr_LNP_type = {
	"&LNP", "&LNP*", 3, { srt_LNP_print, srt_LNP_scan }
};

static sr_type_t *sr_dir_types[] = {
	&sr_dir_type, &sr_LNP_type
};

static sr_type_t sr_ptr_type = {
	"&ptr", "&ptr*", 3, { srt_ptr_print, srt_ptr_scan }
};

static sr_type_t *sr_ptr_types[] = {
	&sr_ptr_type
};

static sr_type_t sr_bool_type = {
	"&bool", "&bool*", 4, { srt_bool_print, srt_bool_scan }
};

static sr_type_t *sr_bool_types[] = {
	&sr_bool_type
};

ecm_ptrs_t	ptrs_null = {
	(void **)sr_null_types, 1, 0, 1
};

ecm_ptrs_t	ptrs_int = {
	(void **)sr_int_types, 4, 0, 4
};

ecm_ptrs_t	ptrs_ll = {
	(void **)sr_ll_types, 1, 0, 1
};

ecm_ptrs_t	ptrs_dbl = {
	(void **)sr_dbl_types, 1, 0, 1
};

ecm_ptrs_t	ptrs_str = {
	(void **)sr_str_types, 2, 0, 2
};

ecm_ptrs_t	ptrs_oct = {
	(void **)sr_oct_types, 1, 0, 1
};

ecm_ptrs_t	ptrs_dir = {
	(void **)sr_dir_types, 2, 0, 2
};

ecm_ptrs_t	ptrs_ptr = {
	(void **)sr_ptr_types, 1, 0, 1
};

ecm_ptrs_t	ptrs_bool = {
	(void **)sr_bool_types, 1, 0, 1
};

ecm_ptrs_t	*sr_types[SR_TYPECLS_EOT] = {
	&ptrs_null,
	&ptrs_int,
	&ptrs_ll,
	&ptrs_dbl,
	&ptrs_str,
	&ptrs_oct,
	&ptrs_dir,
	&ptrs_ptr,
	&ptrs_bool,
};
